﻿using System;
using System.Data.Entity;
using System.IO;
using System.Management.Instrumentation;
using Idea.UnitOfWork.Interface;

namespace Idea.UnitOfWork
{
    /// <summary>
    /// Base implementation of Unit of Work
    /// </summary>
    public class UnitOfWork : IUnitOfWork
    {
        private readonly string _id;
        protected IDbContextFactory DbContextFactory;
        protected IUnitOfWorkManager Manager;
        protected bool Open;

        public DbContext DbContext { get; set; }
        public string Id { get { return _id; } }

        public UnitOfWork(IDbContextFactory factory, IUnitOfWorkManager manager)
        {
            DbContextFactory = factory;
            DbContext = DbContextFactory.Create();
            Manager = manager;
            Manager.AddOpenUnitOfWork(this);

            Open = true;
            _id = Guid.NewGuid().ToString();
        }

        public void Commit()
        {
            if (Open)
            {
                // check if this unit of work is last opened
                var last = Manager.GetCurrentUnitOfWork();
                if (!last.Equals(this))
                {
                    throw new ApplicationException("Try to commit outside Unit of work.");
                }

                if (DbContextFactory == null)
                {
                    throw new InstanceNotFoundException("None instance of DbContexFactory was found.");
                }

                if (DbContext == null)
                {
                    DbContext = DbContextFactory.Create();
                }

                DbContext.SaveChanges();
                Open = false;
            }
            else
            {
                throw new InvalidDataException("Unit of work isn't open.");
            }
        }

        public void Rollback()
        {
            if (Open)
            {
                Open = false;
            }
            else
            {
                throw new InvalidDataException("Unit of work isn't open.");
            }
        }

        public void Dispose()
        {
            if (Open)
            {
                Rollback();
            }
            if (DbContext != null)
            {
                DbContext.Dispose();
            }
            Manager.CloseLastUnitOfWork();
        }

        public override bool Equals(object obj)
        {
            IUnitOfWork argument = obj as IUnitOfWork;
            return argument != null && Id.Equals(argument.Id);
        }
    }
}
